package com.sharp.join.tk.mybatis.framework.join;

import com.sharp.join.tk.mybatis.framework.join.anno.JoinAssociation;
import com.sharp.join.tk.mybatis.framework.join.anno.JoinCollection;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.UnknownTypeHandler;
import tk.mybatis.mapper.MapperException;
import tk.mybatis.mapper.annotation.ColumnType;
import tk.mybatis.mapper.annotation.NameStyle;
import tk.mybatis.mapper.code.Style;
import tk.mybatis.mapper.entity.Config;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.entity.EntityField;
import tk.mybatis.mapper.entity.EntityTable;
import tk.mybatis.mapper.mapperhelper.resolve.DefaultEntityResolve;
import tk.mybatis.mapper.util.SimpleTypeUtil;
import tk.mybatis.mapper.util.SqlReservedWords;
import tk.mybatis.mapper.util.StringUtil;

import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.text.MessageFormat;
import java.util.LinkedHashSet;
import java.util.List;

/**
 * Title: JoinEntityResolve
 * Description: Copyright: Copyright (c) 2019 Company: BHFAE
 *
 * @author Sharp
 * @date 2022/7/4 23:36
 */
@Slf4j
public class JoinEntityResolve extends DefaultEntityResolve {

    public static final String SPLIT = ".";
    public static final String COLUMN_SPLIT = "_";

    public static String buildPrefixFieldName(String fieldName) {
        if (StringUtils.isNotEmpty(fieldName)) {
            return fieldName + ".";
        } else {
            return StringUtils.EMPTY;
        }
    }

    @Override
    public EntityTable resolveEntity(Class<?> entityClass, Config config) {
        return resolveEntity(null, null, entityClass, config);
    }

    public EntityTable resolveEntity(EntityTable entityTable, String fieldName, Class<?> entityClass, Config config) {
        Style style = config.getStyle();
        //style，该注解优先于全局配置
        if (entityClass.isAnnotationPresent(NameStyle.class)) {
            NameStyle nameStyle = entityClass.getAnnotation(NameStyle.class);
            style = nameStyle.value();
        }

        if (StringUtils.isNotEmpty(fieldName)) {
            fieldName = fieldName + ".";
        } else {
            fieldName = StringUtils.EMPTY;
        }
        //创建并缓存EntityTable
        if (entityTable == null) {
            if (entityClass.isAnnotationPresent(Table.class)) {
                Table table = entityClass.getAnnotation(Table.class);
                if (!"".equals(table.name())) {
                    entityTable = new EntityTable(entityClass);
                    entityTable.setTable(table);
                }
            }
            if (entityTable == null) {
                entityTable = new EntityTable(entityClass);
                //可以通过stye控制
                String tableName = StringUtil.convertByStyle(entityClass.getSimpleName(), style);
                //自动处理关键字
                if (StringUtil.isNotEmpty(config.getWrapKeyword()) && SqlReservedWords.containsWord(tableName)) {
                    tableName = MessageFormat.format(config.getWrapKeyword(), tableName);
                }
                entityTable.setName(tableName);
            }
            entityTable.setEntityClassColumns(new LinkedHashSet<>());
            entityTable.setEntityClassPKColumns(new LinkedHashSet<>());
        }
        //处理所有列
        List<EntityField> fields;
        if (config.isEnableMethodAnnotation()) {
            // modify by Sharp for Join Mapper
            fields = JoinFieldHelper.getAll(fieldName, entityClass);
        } else {
            // modify by Sharp for Join Mapper
            fields = JoinFieldHelper.getFields(fieldName, entityClass);
        }
        for (EntityField field : fields) {
            if (field.isAnnotationPresent(JoinAssociation.class)
                    || field.isAnnotationPresent(JoinCollection.class)) {
                // modify by Sharp for join mapper
                resolveEntity(entityTable, field.getName(), field.getJavaType(), config);
            }
            //如果启用了简单类型，就做简单类型校验，如果不是简单类型，直接跳过
            //3.5.0 如果启用了枚举作为简单类型，就不会自动忽略枚举类型
            //4.0 如果标记了 Column 或 ColumnType 注解，也不忽略
            if (config.isUseSimpleType()
                    && !field.isAnnotationPresent(Column.class)
                    && !field.isAnnotationPresent(ColumnType.class)
                    && !(SimpleTypeUtil.isSimpleType(field.getJavaType())
                    ||
                    (config.isEnumAsSimpleType() && Enum.class.isAssignableFrom(field.getJavaType())))) {
                continue;
            }
            processField(entityTable, field, entityClass, config, style);
        }
        //当pk.size=0的时候使用所有列作为主键
        if (entityTable.getEntityClassPKColumns().size() == 0) {
            entityTable.setEntityClassPKColumns(entityTable.getEntityClassColumns());
        }
        entityTable.initPropertyMap();
        return entityTable;
    }

    /**
     * 处理字段
     *
     * @param entityTable
     * @param field
     * @param config
     * @param style
     */
    protected void processField(EntityTable entityTable, EntityField field, Class<?> entityClass, Config config, Style style) {
        //排除字段
        if (field.isAnnotationPresent(Transient.class)) {
            return;
        }
        //Id
        EntityColumn entityColumn = new EntityColumn(entityTable);
        //是否使用 {xx, javaType=xxx}
        entityColumn.setUseJavaType(config.isUseJavaType());
        //记录 field 信息，方便后续扩展使用
        entityColumn.setEntityField(field);
        if (field.isAnnotationPresent(Id.class)) {
            entityColumn.setId(true);
        }
        //Column
        String columnName = null;

        String tableName = JoinEntityHelper.getTableName(entityClass);
        if (StringUtils.isEmpty(tableName)) {
            throw new MapperException("invalid table name for class: " + entityClass.getName());
        }
        if (field.isAnnotationPresent(Column.class)) {
            Column column = field.getAnnotation(Column.class);
            // modify by Sharp for Join Mapper
            columnName = tableName + SPLIT + column.name();
            entityColumn.setUpdatable(column.updatable());
            entityColumn.setInsertable(column.insertable());
        }
        //ColumnType
        if (field.isAnnotationPresent(ColumnType.class)) {
            ColumnType columnType = field.getAnnotation(ColumnType.class);
            //是否为 blob 字段
            entityColumn.setBlob(columnType.isBlob());
            //column可以起到别名的作用
            if (StringUtil.isEmpty(columnName) && StringUtil.isNotEmpty(columnType.column())) {
                // modify by Sharp for Join Mapper
                columnName = tableName + SPLIT + columnType.column();
            }
            if (columnType.jdbcType() != JdbcType.UNDEFINED) {
                entityColumn.setJdbcType(columnType.jdbcType());
            }
            if (columnType.typeHandler() != UnknownTypeHandler.class) {
                entityColumn.setTypeHandler(columnType.typeHandler());
            }
        }
        //列名
        if (StringUtil.isEmpty(columnName)) {
            columnName = StringUtil.convertByStyle(field.getName(), style);
        }
        //自动处理关键字
        if (StringUtil.isNotEmpty(config.getWrapKeyword()) && SqlReservedWords.containsWord(columnName)) {
            columnName = MessageFormat.format(config.getWrapKeyword(), columnName);
        }
        // modify by Sharp for Join Mapper
        entityColumn.setProperty(field.getName());
        entityColumn.setColumn(columnName.replace("`", ""));
        entityColumn.setJavaType(field.getJavaType());
        if (field.getJavaType().isPrimitive()) {
            log.warn("通用 Mapper 警告信息: <[" + entityColumn + "]> 使用了基本类型，基本类型在动态 SQL 中由于存在默认值，因此任何时候都不等于 null，建议修改基本类型为对应的包装类型!");
        }
        //OrderBy
        processOrderBy(entityTable, field, entityColumn);
        //处理主键策略
        processKeyGenerator(entityTable, field, entityColumn);
        entityTable.getEntityClassColumns().add(entityColumn);
        if (entityColumn.isId()) {
            entityTable.getEntityClassPKColumns().add(entityColumn);
        }
    }


}
